package main

import (
	"flag"
	"fmt"
	"html/template"
	"log"
	"net"
	"net/http"
	"net/http/fcgi"
	"os"
	"os/signal"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
	"syscall"
	"time"
)

type Site struct {
	Conf       map[string]string
	Db         *MyDB
	FuncMap    template.FuncMap
	Prefix     string
	BlockCache map[uint32]Block
	bmutex     *sync.RWMutex
	Uniq       *UniqObj
	StaticBase string
	DynBase    string
}

func NewSite(cfg map[string]string) *Site {
	var site1 = &Site{
		Conf:       cfg,
		Db:         NewMyDB(cfg["Addr"], cfg["User"], cfg["Password"], cfg["Database"]),
		FuncMap:    nil,
		Prefix:     cfg["FcgiBase"],
		BlockCache: nil,
		bmutex:     new(sync.RWMutex),
		Uniq:       new(UniqObj),
		StaticBase: "",
		DynBase:    "",
	}
	site1.FuncMap = template.FuncMap{"Root": site1.DocRoot, "Date": GetDate}
	site1.MakeBlockCache()
	site1.Uniq.Reset()
	b1, ok := cfg["BaseDir"]
	if ok {
		site1.DynBase = b1
		site1.StaticBase = b1 + "/www"
	} else {
		site1.DynBase = cfg["DynBase"]
		site1.StaticBase = cfg["StaticBase"]
	}
	return site1
}

func (self *Site) DocRoot() string {
	return self.Conf["StaticRoot"]
}
func GetDate(y, m, d string) string {
	now1 := time.Now()
	//Format "2006-01-02 15:04:05"
	tf1 := fmt.Sprintf("2006%v1%v2%v", y, m, d)
	return now1.Format(tf1)
}

func (self *Site) setHandlers() {
	http.HandleFunc(self.Prefix+"/", self.HandleIndex)
	http.HandleFunc(self.Prefix+"/admin", self.HandleAdmin)
	http.HandleFunc(self.Prefix+"/login", self.HandleLogin)
	http.HandleFunc(self.Prefix+"/logout", self.HandleLogout)
	http.HandleFunc(self.Prefix+"/authors", self.HandleAuthors)
	http.HandleFunc(self.Prefix+"/adduser", self.HandleAdduser)
	http.HandleFunc(self.Prefix+"/blocks", self.HandleBlocks)
	http.HandleFunc(self.Prefix+"/addblock", self.HandleAddblock)
	http.HandleFunc(self.Prefix+"/newfile", self.HandleNewfile)
	http.HandleFunc(self.Prefix+"/savefile", self.HandleSavefile)
	http.HandleFunc(self.Prefix+"/savepublish", self.HandleSavepublish)
	http.HandleFunc(self.Prefix+"/editfile", self.HandleEditfile)
	http.HandleFunc(self.Prefix+"/uploadimg", self.HandleUploadimg)
	http.HandleFunc(self.Prefix+"/viewdoc", self.HandleViewdoc)
	http.HandleFunc(self.Prefix+"/publish", self.HandlePublish)
	http.HandleFunc(self.Prefix+"/renameblock", self.HandleRenameblock)
	http.HandleFunc(self.Prefix+"/blocklist", self.HandleBlocklist)
	http.HandleFunc(self.Prefix+"/savediscuss", self.HandleSavediscuss)
	http.HandleFunc(self.Prefix+"/discussions", self.HandleDiscussions)
	http.HandleFunc(self.Prefix+"/reguser", self.HandleReguser)
	http.HandleFunc(self.Prefix+"/saveuser", self.HandleSaveuser)
	http.HandleFunc(self.Prefix+"/recommand", self.HandleRecommand)
	http.HandleFunc(self.Prefix+"/modifyuserpass", self.HandleModifyuserpass)
	http.HandleFunc(self.Prefix+"/mynewpass", self.HandleMynewpass)
	http.HandleFunc(self.Prefix+"/newname", self.HandleNewname)
	http.HandleFunc(self.Prefix+"/userinfo", self.HandleUserinfo)
	http.HandleFunc(self.Prefix+"/search", self.HandleSearch)
	http.HandleFunc(self.Prefix+"/authorsearch", self.HandleAuthorsearch)
	http.HandleFunc(self.Prefix+"/delete", self.HandleDel)
}

func setLogfile() *os.File {
	exe1, err := os.Executable()
	if err != nil {
		log.Fatal(err)
		return nil
	}
	dir1 := filepath.Dir(exe1)
	name1 := filepath.Base(exe1)
	file1 := filepath.Join(dir1, strings.Replace(name1, ".", "_", -1)+".log")
	f, err := os.OpenFile(file1, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		log.Fatal(err)
	}
	log.SetOutput(f)
	return f
}

func recordPid() {
	fp, _ := os.Create("web.pid")
	defer fp.Close()
	pid1 := strconv.Itoa(os.Getpid())
	fp.Write([]byte(pid1))
}

var c chan os.Signal = make(chan os.Signal, 1)

func waitSig() {
	signal.Notify(c, os.Interrupt, os.Kill, syscall.SIGTERM, syscall.SIGABRT, syscall.SIGQUIT)
	//rpcs.InitRpcs(c)
	time.Sleep(time.Second * 5)
	recordPid()
	s := <-c
	log.Println("Exit Signal:", s)
}
func HandleQuitServ(resp http.ResponseWriter, req *http.Request) {
	if strings.HasPrefix(req.RemoteAddr, "127.0.0.1:") {
		c <- os.Interrupt
	}
}

func setQuitHandler(control string) {
	http.HandleFunc(fmt.Sprintf("%v/%v_20180516231942", control, os.Getpid()), HandleQuitServ)
}

func main() {
	var host string
	var d = flag.Bool("d", false, "debug模式，日志输出到屏幕")
	flag.StringVar(&host, "host", "127.0.0.1:9990", "服务地址，默认值为127.0.0.1:9990，例如：127.0.0.1:9990。注：程序优先使用配置文件内的FcgiAddr。")
	flag.Parse()
	if *d == false {
		f := setLogfile()
		defer f.Close()
	}
	//chdir
	exe1, _ := os.Executable()
	dir1 := filepath.Dir(exe1)
	os.Chdir(dir1)

	//conf
	var cfg = readToml("web.toml")
	if cfg == nil {
		panic("err readToml")
	}
	var sites = make(map[string]*Site)
	for k, conf := range cfg.Sites {
		//init site ,db, block cache
		sites[k] = NewSite(conf)
		defer sites[k].Db.Close()
	}

	//determine bind address , config file first
	bindaddr := cfg.FcgiAddr
	if bindaddr == "" {
		bindaddr = host
	}
	listener, _ := net.Listen("tcp", bindaddr)
	defer listener.Close()
	log.Printf("Bind: %v\n", bindaddr)

	log.Println("fcgi run")
	for _, v := range sites {
		v.setHandlers()
	}
	setQuitHandler(cfg.Control)

	go fcgi.Serve(listener, nil)

	waitSig()
	os.Remove("web.pid")
	log.Println("remove web.pid")
}
